package org.dh2580.netty.demo.aio;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

/**
 * @Author: hao.deng
 * @Date: 2019/3/27-下午12:52
 */
public class TimeServer {
    public static void main(String[] args) throws IOException {
        new Thread(new AsyncTimeServerHandler(8080)).start();
    }

    public static class AsyncTimeServerHandler implements Runnable {
        private int port;

        CountDownLatch latch;

        AsynchronousServerSocketChannel asynchronousServerSocketChannel;

        public AsyncTimeServerHandler(int port) throws IOException {
            this.port = port;

            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
            asynchronousServerSocketChannel.bind(new InetSocketAddress(this.port));
            System.out.println("Time server is start in port: " + port);
        }

        @Override
        public void run() {
            latch = new CountDownLatch(1);
            doAccept();
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void doAccept() {
            asynchronousServerSocketChannel.accept(this, new CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHandler>() {
                @Override
                public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) {
                    //循环接受连接
                    attachment.asynchronousServerSocketChannel.accept(attachment, this);

                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    result.read(buffer, buffer, new ReadCompletionHandler(result));
                }

                @Override
                public void failed(Throwable exc, AsyncTimeServerHandler attachment) {
                    exc.printStackTrace();
                    attachment.latch.countDown();
                }
            });
        }
    }

    public static class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
        private AsynchronousSocketChannel socketChannel;

        public ReadCompletionHandler(AsynchronousSocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            try {
                attachment.flip();
                byte[] body = new byte[attachment.remaining()];
                attachment.get(body);


                String req = new String(body, "UTF-8");
                System.out.println("Time server receive order : " + req);

                String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(req) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
                doWrite(currentTime);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

        }

        private void doWrite(String currentTime) {
            if (currentTime == null || currentTime.trim().length() == 0) {
                return;
            }
            byte[] bytes = currentTime.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();

            socketChannel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                    if (writeBuffer.hasRemaining()) {
                        socketChannel.write(writeBuffer, writeBuffer, this);
                    }
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                    try {
                        socketChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            try {
                this.socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
